package query.insert

import ast.expr.SqlIdentifierExpr
import ast.statement.insert.SqlInsert
import database.DB
import dsl.Query
import query.ReviseQuery
import query.select.Select
import query.select.SelectQuery
import util.toSqlString
import visitor.getExpr
import visitor.getQueryExpr
import java.sql.Connection

/**
 * 无实体类的insert语句dsl类
 * @property db DB 数据库类型
 * @property conn Connection? 数据库连接
 * @property isTransaction Boolean 是否是事务
 * @property sqlInsert SqlInsert insert语法树
 * @property columns MutableList<String> 插入字段名列表
 * @property records MutableList<MutableMap<String, Any?>> 插入记录列表
 */
class NativeInsert(
    var db: DB = DB.MYSQL,
    override var conn: Connection? = null,
    override var isTransaction: Boolean = false
) : ReviseQuery() {
    constructor(db: DB) : this(db, null, false)

    private var sqlInsert = SqlInsert()

    private var columns = mutableListOf<String>()

    private var records = mutableListOf<MutableMap<String, Any?>>()

    /**
     * insert into子句
     * 例如：NativeInsert() into "t1"
     * @param table String 表名
     * @return NativeInsert 插入dsl
     */
    infix fun into(table: String): NativeInsert {
        sqlInsert.table = SqlIdentifierExpr(table)
        return this
    }

    /**
     * insert语句的字段名
     * 例如：NativeInsert() into "t1" columns listOf("c1", "c2")
     * @param columns List<String> 字段名列表
     * @return NativeInsert 插入dsl
     */
    infix fun columns(columns: List<String>): NativeInsert {
        this.columns = columns.toMutableList()
        this.sqlInsert.columns.addAll(columns.map { SqlIdentifierExpr(it) })
        return this
    }

    /**
     * insert语句的字段名
     * 例如：NativeInsert().into("t1").columns("c1", "c2")
     * @param column Array<out String> 字段名列表
     * @return NativeInsert 插入dsl
     */
    fun columns(vararg column: String): NativeInsert {
        this.columns = column.toMutableList()
        this.sqlInsert.columns.addAll(columns.map { SqlIdentifierExpr(it) })
        return this
    }

    /**
     * insert语句的子查询
     * @param select [@kotlin.ExtensionFunctionType] Function1<Select, SelectQuery> 子查询
     * @return NativeInsert 插入dsl
     */
    infix fun select(select: Select.() -> SelectQuery): NativeInsert {
        this.sqlInsert.query = select(Select(db)).getSelect()
        return this
    }

    /**
     * insert语句的values子句（单条）
     * 例如：NativeInsert() into "t1" columns listOf("c1", "c2") value mutableMapOf("c1" to 1, "c2" to "xxx", "c3" to null)
     * @param value MutableMap<String, Any?> 一条插入的数据
     * @return NativeInsert 插入dsl
     */
    infix fun value(value: MutableMap<String, Any?>): NativeInsert {
        if (columns.isEmpty()) {
            value.forEach {
                sqlInsert.columns.add(SqlIdentifierExpr(it.key))
                columns.add(it.key)
            }
        }

        sqlInsert.values.add(columns.map {
            when (val cell = value[it]) {
                is Query -> getQueryExpr(cell, this.db).expr
                else -> getExpr(cell)
            }
        })

        records.add(value)

        return this
    }

    /**
     * insert语句的values子句（批量）
     * 例如：NativeInsert() into "t1" columns listOf("c1", "c2") values listOf(mutableMapOf("c1" to 1, "c2" to "xxx"), mutableMapOf("c1" to 2, "c2" to "yyy"))
     * @param values List<MutableMap<String, Any?>> 插入的数据列表
     * @return NativeInsert 插入dsl
     */
    infix fun values(values: List<MutableMap<String, Any?>>): NativeInsert {
        values.forEach {
            value(it)
        }

        return this
    }

    /**
     * insert语句的values子句（批量）
     * 例如：NativeInsert().into("t1").columns("c1", "c2").values(mutableMapOf("c1" to 1, "c2" to "xxx"), mutableMapOf("c1" to 2, "c2" to "yyy"))
     * @param values MutableMap<String, Any?> 插入的数据列表
     * @return NativeInsert 插入dsl
     */
    fun values(vararg values: MutableMap<String, Any?>): NativeInsert {
        return values(values.toList())
    }

    /**
     * 生成sql语句
     * @return String 生成的sql
     */
    override fun sql() = toSqlString(sqlInsert, db)

    /**
     * 执行语句并返回受影响行数（如果有自增主键会在原插入的数据里添加一个incrKey的键）
     * @return Int 受影响行数
     */
    override fun exec(): Int {
        val result = database.execReturnKey(conn!!, this.sql())
        if (!isTransaction) {
            conn!!.close()
        }

        if (records.isNotEmpty()) {
            result.forEachIndexed { index, item ->
                records[index]["incrKey"] = item
            }
        }
        return records.size
    }

    /**
     * 执行语句并返回自增主键列表
     * @return List<Long> 自增主键列表
     */
    fun execReturnKey(): List<Long> {
        val result = database.execReturnKey(conn!!, this.sql())
        if (!isTransaction) {
            conn!!.close()
        }

        return result
    }
}